﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using MVVMFoundation;

namespace DictationToolSilverlight.ViewModel
{
    public class MediaPlayerViewModel : ObservableObject
    {
        public MediaPlayerViewModel()
        {
            MediaElement = new MediaElement();

            MediaElement.AutoPlay = false;
            MediaElement.BufferingProgressChanged += new RoutedEventHandler(MediaElement_BufferingProgressChanged);
            MediaElement.DownloadProgressChanged += new RoutedEventHandler(MediaElement_DownloadProgressChanged);
            MediaElement.MediaOpened += new RoutedEventHandler(MediaElement_MediaOpened);
            MediaElement.MediaFailed += new EventHandler<ExceptionRoutedEventArgs>(MediaElement_MediaFailed);
            MediaElement.MediaEnded += new RoutedEventHandler(MediaElement_MediaEnded);
            MediaElement.CurrentStateChanged += new RoutedEventHandler(MediaElement_CurrentStateChanged);
            CompositionTarget.Rendering +=new EventHandler(CompositionTarget_Rendering);
        }

        public MediaElement MediaElement { get; set; }

        public string SourceUrl
        {
            get { return sourceUrl; }
            set
            {
                sourceUrl = value;
                MediaElement.Source = new Uri(sourceUrl, UriKind.Absolute);
            }
        }

        public double Buffer
        {
            get { return buffer; }
            private set
            {
                if (buffer == value)
                    return;
                buffer = value;
                base.RaisePropertyChanged("Buffer");
            }
        }

        public double Process
        {
            get { return process; }
            private set
            {
                if (process == value)
                    return;
                process = value;
                base.RaisePropertyChanged("Process");
            }
        }

        public bool Muted
        {
            get { return !MediaElement.IsMuted; }
            set
            {
                if (MediaElement.IsMuted == !value)
                    return;
                MediaElement.IsMuted = !value;
                base.RaisePropertyChanged("Muted");
            }
        }

        public TimeSpan TotalTime
        {
            get { return totalTime; }
            private set
            {
                if (totalTime == value)
                    return;
                totalTime = value;
                base.RaisePropertyChanged("TotalTime");
            }
        }

        public bool IsPlaying
        {
            get { return isPlaying; }
            set
            {
                if (isPlaying == value)
                    return;
                isPlaying = value;
                base.RaisePropertyChanged("IsPlaying");
            }
        }


        public ICommand PlayPauseCommand
        {
            get
            {
                return new RelayCommand(() => { PlayPause(); });
            }
        }

        public ICommand StopCommand
        {
            get
            {
                return new RelayCommand(Stop);
            }
        }

        public ICommand StartCommand
        {
            get
            {
                return new RelayCommand(SetStart);
            }
        }

        public ICommand RepeatCommand
        {
            get
            {
                return new RelayCommand(Repeat);
            }
        }

        public ICommand ContinueCommand
        {
            get
            {
                return new RelayCommand(Continue);
            }
        }

        public double StartPosition
        {
            get { return startPosition; }
            private set
            {
                if (startPosition == value)
                    return;
                startPosition = value;
                base.RaisePropertyChanged("StartPosition");
            }
        }

        public double EndPosition
        {
            get { return endPosition; }
            private set
            {
                if (endPosition == value)
                    return;
                endPosition = value;
                base.RaisePropertyChanged("EndPosition");
            }
        }

        public bool Repeating
        {
            get { return repeating; }
            private set
            {
                repeating = value;
                base.RaisePropertyChanged("Repeating");
            }
        }

        #region Methods

        public void SeekTo(double seekTo)
        {
            MediaElement.Position = TimeSpan.FromSeconds(seekTo * MediaElement.NaturalDuration.TimeSpan.TotalSeconds);
            SetStart();
        }

        private void PlayPause(bool isContinue = false)
        {
            if (MediaElement.CurrentState == MediaElementState.Paused || MediaElement.CurrentState == MediaElementState.Stopped || isContinue)
            {
                MediaElement.Play();
                Repeating = false;
            }
            else if (MediaElement.CurrentState == MediaElementState.Playing)
            {
                MediaElement.Pause();
            }
            else
            {
                IsPlaying = false;
            }
            startTime = MediaElement.Position;
            StartPosition = GetProcess();
        }

        private void Stop()
        {
            MediaElement.Stop();
        }

        private void SetStart()
        {
            startTime = MediaElement.Position;
            StartPosition = GetProcess();
            if (Repeating && startTime >= endTime)
            {
                Repeating = false;
            }
        }

        private void Repeat()
        {
            if (Repeating)
            {
                MediaElement.Position = startTime;
            }
            else
            {
                EndPosition = GetProcess();
                endTime = MediaElement.Position;
                MediaElement.Position = startTime;
                Repeating = true;
            }
            MediaElement.Play();
        }

        private void Continue()
        {
            PlayPause(true);
        }

        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            Process = GetProcess();
            if (Repeating && MediaElement.Position.TotalMilliseconds >= endTime.TotalMilliseconds)
            {
                MediaElement.Pause();
            }
        }

        private double GetProcess()
        {
            return MediaElement.Position.TotalSeconds / MediaElement.NaturalDuration.TimeSpan.TotalSeconds * (App.Current.Host.Content.ActualWidth - 20.0);
        }

        private void MediaElement_MediaOpened(object sender, RoutedEventArgs e)
        {
            TotalTime = MediaElement.NaturalDuration.TimeSpan;
        }

        private void MediaElement_MediaFailed(object sender, ExceptionRoutedEventArgs e)
        {
        }

        private void MediaElement_BufferingProgressChanged(object sender, RoutedEventArgs e)
        {
            if (MediaElement.BufferingProgress != 1)
            {
                Buffer = MediaElement.BufferingProgress * (App.Current.Host.Content.ActualWidth - 20);
            }
        }

        private void MediaElement_DownloadProgressChanged(object sender, RoutedEventArgs e)
        {
            Buffer = MediaElement.DownloadProgress * (App.Current.Host.Content.ActualWidth - 20);
        }

        private void MediaElement_CurrentStateChanged(object sender, RoutedEventArgs e)
        {
            IsPlaying = (MediaElement.CurrentState == MediaElementState.Playing);
        }

        private void MediaElement_MediaEnded(object sender, RoutedEventArgs e)
        {
            Stop();
        }

        #endregion

        #region Fields

        private bool isPlaying;
        private bool repeating;
        private double buffer;
        private double process;
        private double startPosition;
        private double endPosition;
        private TimeSpan startTime;
        private TimeSpan endTime;
        private TimeSpan totalTime;
        private string sourceUrl;

        #endregion
    }
}
